package org.arl.modem.linktuner;

import org.arl.modem.*;
import org.arl.modem.link.*;
import org.arl.modem.*;
import org.arl.modem.phy.*;
import jade.core.*;

public class linkTunerCommands extends CommandHandler {
	
	private AID lt;
	private AID phy;
	private AID link;
	
	public int enable=1;
	public static int batchSize=4;
	public static int enableCycleTest=1;		//if =0, will not change scheme params between test packet trains
		
	public final static int BEGIN_LT_COMMAND=1;
	public final static int E_LT_COMMAND=2;
	public final static int INIT_LT_COMMAND=3;
	public final static int E_RESET_COMMAND=4;

	public linkTunerCommands(ATCommandInterpreter agent) {
		super("~E", agent);
//		agent.subscribeTopic(modem_example.NOTIFICATIONS);
		agent.subscribeTopic(Link.NOTIFICATIONS);
	   // agent.subscribeTopic(Physical.NOTIFICATIONS);  //experimental, trying to read phy notifications
		lt = new AID(agent.getAgentProperty("lt", "lt"), AID.ISLOCALNAME);
	    phy = new AID(agent.getAgentProperty("phy", "phy"), AID.ISLOCALNAME);
	    //the local variable link now holds the identifier info for the agent lnk. In case you have more that a single link layer 
	    //agent, you can associate a particular link layer agent by writing lt.lnk=lnk1 in the file modem.properties
		link = new AID(agent.getAgentProperty("lnk", "lnk"), AID.ISLOCALNAME);
	}
	
	@ATCmd("Experimental link tuning command")
	public boolean BEGIN_LTz(ATCommand c){
		FAPIMessage msg=agent.createMessage(FAPIMessage.REQUEST,lt);
		msg.add("linkTuningCommands",BEGIN_LT_COMMAND);
		System.out.println("Inside the BEGIN_LT method");
		return sendAndCheckAck(msg);
	}
	
	public boolean E_LTz(ATCommand c){
		FAPIMessage msg=agent.createMessage(FAPIMessage.REQUEST,lt);
		msg.add("linkTuningCommands",E_LT_COMMAND);
		log.fine("Inside the E_LT method");
		return sendAndCheckAck(msg);
	}
	
	public boolean INIT_LTz(ATCommand c){
		FAPIMessage msg=agent.createMessage(FAPIMessage.REQUEST,lt);
		msg.add("linkTuningCommands",INIT_LT_COMMAND);
		log.fine("Inside the INIT_LT method");
		return sendAndCheckAck(msg);

	}
	
	public boolean E_RESETz(ATCommand c){
		FAPIMessage msg=agent.createMessage(FAPIMessage.REQUEST,lt);
		msg.add("linkTuningCommands",E_RESET_COMMAND);
		log.fine("Inside the E_RESET method");
		return sendAndCheckAck(msg);
	}	
	
	@ATCmd("AT~EE.param=value - set parameter\n"
	        +"AT~EE.param? - get parameter")
	  /// @see linkTuner.linkTunerParam for list of supported parameters.
	public boolean E_(ATCommand c) {
		log.fine("entered reflected function");
		return handleLocalParams(lt, linkTunerParam.class, c);
	  }
	
	//modified the handleParams() method from command handler.
	 private boolean handleLocalParams(AID aid, Class cls, ATCommand cmd) {
		 log.fine("entered handleParamsLocalCopy");
	    try {
	      String s = cmd.line.substring(6);
	      if (s.endsWith("?")) {
	        // get
	        int p = parseParam(cls, s.substring(0, s.length()-1));
	        if (p < 0) return false;
	        int v = getSetLocalParams(aid, p, -1);
	        if (v < 0) return false;
	        output("link tuner param "+cmd.prefix+"E."+p+"="+v);
	        return true;
	      } else {
	        // set
	        String[] pv = s.split("=", 2);
	        log.fine("pv = "+pv[0]);
	        int p = parseParam(cls, pv[0]);
	        if (p < 0) return false;
	        int v = Utils.parseInt(pv[1]);
	        v = getSetLocalParams(aid, p, v);
	        if (v < 0) return false;
	        output("link tuner param "+cmd.prefix+"E."+p+"="+v);
	        return true;
	      }
	    } catch (NumberFormatException ex) {
	      // do nothing
	    }
	    return false;
	  }

	 private int getSetLocalParams(AID aid, int p, int v) {
		  if(v<0){
			  switch (p){
			  	case linkTunerParam.ENABLE:
			  		log.fine("queried for link tuner parameter ENABLE. current value = "+enable);
			  		return enable;
			  	case linkTunerParam.MAX_TEST_PKT_TRAIN_CNT:
			  		log.fine("queried for link tuner parameter MAX_TEST_PKT_TRAIN_CNT. current value = "+linkTuner.maxTestPktTrainCnt);
			  		return linkTuner.maxTestPktTrainCnt;
			  	case linkTunerParam.BATCH_SIZE:
			  		log.fine("queried for link tuner parameter BATCH_SIZE. current value = "+batchSize);
			  		return batchSize;
			  	case linkTunerParam.ENABLE_CYCLE_TEST:
			  		log.fine("queried for link tuner parameter ENBLE_CYCLE_TEST. current value = "+enableCycleTest);
			  		return enableCycleTest;
			  	default:
			  		log.fine("queried for invalid link tuner parameter");
			  		return -1;
				  }
		  }
		  else{
			  switch (p){
			  	case linkTunerParam.ENABLE:
			  		enable=v;
			  		log.fine("link tuner parameter ENABLE is now set to "+enable);  
			  		return enable;
			  	case linkTunerParam.MAX_TEST_PKT_TRAIN_CNT:
			  		if(v<linkTuner.schemeParamSuperSet.size()){
			  			linkTuner.maxTestPktTrainCnt=v;
				  		return linkTuner.maxTestPktTrainCnt;
			  		}else{
			  			output("maxTestPktTrainCnt cannot exceed schemeParamSuperSet.size()");
			  			log.fine("maxTestPktTrainCnt cannot exceed schemeParamSuperSet.size(), so could not set value");
			  			return linkTuner.maxTestPktTrainCnt;
			  		}
			  	case linkTunerParam.BATCH_SIZE:
			  		batchSize=v;
			  		log.fine("link tuner parameter BATCH_SIZE is now set to "+batchSize);
			  		return batchSize;
			  	case linkTunerParam.ENABLE_CYCLE_TEST:
			  		enableCycleTest=v;
			  		log.fine("link tuner parameter ENABLE_CYCLE_TEST is now set to "+enableCycleTest);
			  		return enableCycleTest;
			  	default:
			  		log.fine("invalid parameter chosen for param set");
			  		return -1;
			  }
		  }	
	  }

	
	
	//processNotification can and must return true iff you have successfully processed the packet, coz returning
	//true will not invoke any other process notification, possibly resulting in nulls etc. 
	protected boolean processNotification(FAPIMessage msg) {
			//note that msg.getAsInteger("protocol") will return an Integer class in the absence of a def value. Trying to compare a 
			//null integer class with something will cause a null pointer exception. Hence, always specify a def value of -1, since
			//you know that there is no protocol ID with a value of -1...
			if(msg.getPerformative()==FAPIMessage.INFORM && msg.getAsInteger("protocol",-1)==linkTuner.LINK_TUNER_PROTOCOL_ID){
				//log.fine("processNotification of lt invoked");
				byte[] data = msg.getData();
				if (data!=null && data.length > 0){
					//log.fine("got valid data"+ (byte)data[0]+" , "+ (long)data[1]+" , "+ (byte)data[2]+" , "+ (byte)data[3]+" , ");
					switch ((byte)data[0]){
						case (byte)linkTuner.TYPE_ACTIVATE_SCHEME:
							log.fine("proceeding to activate scheme");
							process(linkTuner.TYPE_ACTIVATE_SCHEME,msg);
							break;
						case (byte)linkTuner.TYPE_SET_SCHEME_PARAM_M:
							log.fine("proceeding to set scheme parameters");
							log.fine("in process notification, msg.getData().length = "+msg.getData().length);
							process(linkTuner.TYPE_SET_SCHEME_PARAM_M, msg);
							break;
						case (byte)linkTuner.TYPE_SEND_SCHEME_PARAM_ACK_S:
							log.fine("master has recieved back an ack for a setSchemeParam request");
							process(linkTuner.TYPE_SEND_SCHEME_PARAM_ACK_S, msg);
							break;
						case (byte)linkTuner.TYPE_QUERY_TEST_RESULTS_M:
							log.fine("received a test result query from master");
							process(linkTuner.TYPE_QUERY_TEST_RESULTS_M,msg);
							break;
						case (byte)linkTuner.TYPE_TEST_RESULT_QUERY_ACK_S:
							process(linkTuner.TYPE_TEST_RESULT_QUERY_ACK_S,msg);
							break;
					}
				}
			return true;
		}
		else return false;
	}
		
	private void process(int type, FAPIMessage msg){
		FAPIMessage req=agent.createMessage(FAPIMessage.REQUEST,lt);
		req.add("linkTuningCommands",type);
		req.setData(msg.getData());
		if(sendAndCheckAck(req)){
			log.fine("asked the lt agent to process message of type "+type);
		}
	}
}